﻿using Microsoft.DirectX;
using System;
using System.Collections;
using System.IO;
using System.Text;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectSound;
using System.Drawing;

namespace WiiShooter
{
    class Projectile
    {
        private Microsoft.DirectX.Direct3D.Device device;
        //owner of this projectile
        private Entity owner;
        public ArrayList spriteName;
        public ArrayList sounds;
        public ArrayList spriteTextures = new ArrayList();
        private ArrayList spriteDeathTextures = new ArrayList();
        private ArrayList spriteDeathTextureNames = new ArrayList();
        public CustomVertex.PositionNormalTextured[] spriteArray;
        public Vector3 centerCoords;
        private Vector3 moveCoords;
        public int damage;
        public float width;
        public float height;
        public float speed;
        public float theAngle;
        public float scale;
        //text file to load
        public String name;
        private float scaledSizeX, scaledSizeY;
        public bool removeMe = false;
        public Texture texture;
        private int animateIndex = 0;
        private Microsoft.DirectX.DirectSound.Device soundDevice;
        private Boolean deathAnimate = false;
        private Boolean canHit = true;
        private int deathIndex = 0;
        //Create and setup the buffer description.
        private BufferDescription buffer_desc = new BufferDescription();
        public SecondaryBuffer buffer;
        private String soundEffectName;
        private BufferDescription buffer_desc2 = new BufferDescription();
        public SecondaryBuffer buffer2;
        private String shootSound;

        public Vector3 positionToHit;

        public Projectile(Entity owner, String FileName, ref Microsoft.DirectX.Direct3D.Device devicePass,
            ref Microsoft.DirectX.DirectSound.Device soundDevicePass)
        {
            this.device = devicePass;
            this.owner = owner;
            soundDevice = soundDevicePass;
            //initialize sound
            buffer_desc.ControlEffects = true; //this has to be true to use effects.
            buffer_desc.GlobalFocus = true; //play sound even if application loses focus.
            buffer_desc2.ControlEffects = true;
            buffer_desc2.GlobalFocus = true;
            theAngle = 0;
            spriteArray = new CustomVertex.PositionNormalTextured[6];
            centerCoords.X = owner.centerCoords.X;
            centerCoords.Y = owner.centerCoords.Y;
            centerCoords.Z = owner.centerCoords.Z;
            name = FileName;
            //moveCoords = new Vector3(0, 0, 0);
            positionToHit = new Vector3(0, 0, 0);
            //calculate the amount to move
            readFile(FileName);
            scaledSizeX = width * scale;
            scaledSizeY = height * scale;
            calculateMoveData();
            checkWalls();
            if(owner.theName.Equals("Player.txt"))
                playShootSound();
        }

        private void playShootSound()
        {
            buffer2.Play(0, BufferPlayFlags.Default);
        }


        /**
         * This method calculates how much the projectile will move per clock tick
         * if the projectile orginated from a monster
         */ 
        private void calculateMoveData()
        {

            if (!owner.theName.Equals("Player.txt")){
                //for monsters, this will create a unit vector from monster
                //to player and set it as the movecoords vector3

                float x = Game.player.centerCoords.X - owner.centerCoords.X;
                float y = Game.player.centerCoords.Y - owner.centerCoords.Y;
                float z = Game.player.centerCoords.Z - owner.centerCoords.Z;

                float distance = (float)Math.Sqrt((double)(x * x + y * y + z * z));

                //since we want the vector's magnitude to be 1, each component must be
                //divided by the distance, so x/distance is the amount moved by x each unit.
                //this ensures that the projectile is moving only one unit at a time.
                //we then will multiply each by the speed, so that the projectile
                //will move at the correct speed.

                
                x = x / distance * speed;
                y = y / distance * speed;
                z = z / distance * speed;

                moveCoords = new Vector3(x, y, z);  
            }

            else if (owner.theName.Equals("Player.txt"))
            {
                //code for the player projectile (moved from the constructor to here
                float x = Game.camera.cameraTarget.X - owner.centerCoords.X;
                float y = Game.camera.cameraTarget.Y - owner.centerCoords.Y;
                float z = Game.camera.cameraTarget.Z - owner.centerCoords.Z;

                double theta = Math.Atan2((double)x, (double)z);

                double phi = Math.Acos((double)y);

                theta = theta + (double)(Game.reticlePosition.X / Game.screenResolutionWidth - .5);
                phi = phi + (double)(Game.reticlePosition.Y / Game.screenResolutionHeight - .5);
                //convert spherical to cartesian


                z = (float)(Math.Cos(theta) * Math.Sin(phi)) * speed;
                x = (float)(Math.Sin(theta) * Math.Sin(phi)) * speed;
                y = (float)(Math.Cos(phi)) * speed;

                moveCoords = new Vector3(x, y, z);
            }



        }

        //read the information from the text file to 
        private void readFile(String FileName)
        {
            StreamReader objReader = new StreamReader(FileName);
            string sLine = "";
            spriteName = new ArrayList();
            sounds = new ArrayList();

            // Start read ye olde texte file(e).
            while (sLine != null)
            {
                sLine = objReader.ReadLine();
                if (sLine != null)
                {
                    if (sLine.StartsWith("Sprite"))
                    {
                        string[] inputs = sLine.Split(',');
                        for (int i = 1; i < inputs.Length; i++)
                            spriteName.Add(inputs[i]);
                    }
                    if (sLine.StartsWith("Death"))
                    {
                        string[] inputs = sLine.Split(',');
                        for (int i = 1; i < inputs.Length; i++)
                            spriteDeathTextureNames.Add(inputs[i]);
                    }
                    if (sLine.StartsWith("Speed"))
                    {
                        string[] inputs = sLine.Split(',');
                        speed = float.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Sound")) {
                        string[] inputs = sLine.Split(',');
                        soundEffectName = inputs[1];
                    }
                    if (sLine.StartsWith("Width"))
                    {
                        string[] inputs = sLine.Split(',');
                        width = float.Parse(inputs[1]);
                    }
                    if (sLine.StartsWith("Height"))
                    {
                        string[] inputs = sLine.Split(',');
                        height = float.Parse(inputs[1]);
                    }
                    if (sLine.StartsWith("Scale"))
                    {
                        string[] inputs = sLine.Split(',');
                        scale = float.Parse(inputs[1]);
                    }
                    if (sLine.StartsWith("Damage"))
                    {
                        string[] inputs = sLine.Split(',');
                        damage = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("ShootSound")) {
                        string[] inputs = sLine.Split(',');
                        shootSound = inputs[1];
                    }
                }
            }
            objReader.Close();
            loadSprites();
            loadSounds();
        }

        //load the sounds that the projectile will use
        private void loadSounds()
        {
            
            buffer = new SecondaryBuffer(soundEffectName, buffer_desc, soundDevice);
            if (owner.theName.Equals("Player.txt")){
            buffer2 = new SecondaryBuffer(shootSound, buffer_desc2, soundDevice);
            }
        }

        //load the sprites for the projectile
        private void loadSprites()
        {
            /* put all the textures into memory */
            for (int i = 0; i < spriteName.Count; i++)
            {
                spriteTextures.Add(TextureLoader.FromFile(device, (string)spriteName[i]));
            }
            for (int i = 0; i < spriteDeathTextureNames.Count; i++)
            {
                spriteDeathTextures.Add(TextureLoader.FromFile(device, (string)spriteDeathTextureNames[i]));
            }
            texture = (Texture)spriteTextures[0];
        }

        /**
         * Change the texture the projectile is using for animation purposes
         */ 
        private void animate()
        {
            if (animateIndex < spriteTextures.Count - 1)
            {
                animateIndex++;
            }
            else
            {
                animateIndex = 0;
            }
            texture = (Texture)spriteTextures[animateIndex];
        }

        private void death()
        {
            moveCoords.X = 0;
            moveCoords.Y = 0;
            moveCoords.Z = 0;
            if (deathIndex < spriteDeathTextures.Count - 1)
            {
                buffer.Play(0, BufferPlayFlags.Default);
                deathIndex++;
            }
            else
            {
                removeMe = true;
            }
            texture = (Texture)spriteDeathTextures[deathIndex];
        }

        /**
         * Move the projectile
         */ 
        public void move()
        {
            animate();
            // Check for hit detection.
            hitDetection();
            if (deathAnimate)
            {
                death();
            }

                centerCoords.X += moveCoords.X;
                centerCoords.Y += moveCoords.Y;
                centerCoords.Z += moveCoords.Z;
            
            Vector3 vector3one = new Vector3(centerCoords.X - (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y - (scaledSizeY / 2), centerCoords.Z - (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3two = new Vector3(centerCoords.X + (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y - (scaledSizeY / 2), centerCoords.Z + (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3three = new Vector3(centerCoords.X + (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y + (scaledSizeY / 2), centerCoords.Z + (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3four = new Vector3(centerCoords.X - (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y + (scaledSizeY / 2), centerCoords.Z - (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            spriteArray[0] = new CustomVertex.PositionNormalTextured(vector3four, new Vector3(0, 0, 1f), 0, 0);
            spriteArray[1] = new CustomVertex.PositionNormalTextured(vector3one, new Vector3(0, 0, 1f), 0, 1f);
            spriteArray[2] = new CustomVertex.PositionNormalTextured(vector3two, new Vector3(0, 0, 1f), 1f, 1f);
            spriteArray[3] = new CustomVertex.PositionNormalTextured(vector3two, new Vector3(0, 0, 1f), 1f, 1f);
            spriteArray[4] = new CustomVertex.PositionNormalTextured(vector3four, new Vector3(0, 0, 1f), 0, 0);
            spriteArray[5] = new CustomVertex.PositionNormalTextured(vector3three, new Vector3(0, 0, 1f), 1f, 0);

            hitDetection();
            hitWall();
        }

        /**
         * Detect if this projectile has hit an entity
         */ 
        public void hitDetection()
        {
            bool hitX = false, hitY = false, hitZ = false;

            if (!owner.theName.Equals("Player.txt"))
            {
                if (Math.Abs(centerCoords.X - Game.player.centerCoords.X) < (Game.player.theWidth / 2))
                {
                    hitX = true;
                }
                if (Math.Abs(centerCoords.Y - Game.player.centerCoords.Y) < (Game.player.theHeight / 2))
                {
                    hitY = true;
                }
                if (Math.Abs(centerCoords.Z - Game.player.centerCoords.Z) < (Game.player.theWidth / 2))
                {
                    hitZ = true;
                }
                if (hitX && hitY && hitZ)
                {
                    if (canHit)
                    {
                        Game.player.theHealth -= damage;
                        hitX = false;
                        hitY = false;
                        hitZ = false;
                        
                        deathAnimate = true;
                        canHit = false;
                    }
                }
                
            }
            else  //check for collisions with monsters
            {
                foreach (Entity t in Game.entityList)
                {
                    if (!t.theName.Equals("Player.txt"))
                    {
                        if (Math.Abs(centerCoords.X - t.centerCoords.X) < ((t.theWidth *.5f)*t.theScale))
                        {
                            hitX = true;
                        }
                        if (Math.Abs(centerCoords.Y - t.centerCoords.Y) < ((t.theHeight *.5f)*t.theScale))
                        {
                            hitY = true;
                        }
                        if (Math.Abs(centerCoords.Z - t.centerCoords.Z) < ((t.theWidth *.5f)*t.theScale))
                        {
                            hitZ = true;
                        }
                        if(hitX && hitY && hitZ)
                        {
                            if (canHit)
                            {
                                t.theHealth -= damage;
                                hitX = false;
                                hitY = false;
                                hitZ = false;
                                

                                deathAnimate = true;
                                //Console.WriteLine("Hit " + t.theName);
                                canHit = false;
                            }
                        }
                    }
                }
            }
        }

        private void checkWalls()
        {

            Matrix a = new Matrix();
            Matrix b = new Matrix();
            // t = - |b| / |a|

            a.M11 = 1;
            a.M12 = 1;
            a.M13 = 1;
            a.M14 = 0;
            b.M11 = 1;
            b.M12 = 1;
            b.M13 = 1;
            b.M14 = 1;

            float t = float.MaxValue;
            float t2 = float.MaxValue;

            foreach (Wall w in Game.level.wallList)
            {
                b.M21 = a.M21 = w.vertex1.X;
                b.M22 = a.M22 = w.vertex2.X;
                b.M23 = a.M23 = w.vertex3.X;
                b.M24 = this.owner.centerCoords.X;

                b.M31 = a.M31 = w.vertex1.Y;
                b.M32 = a.M32 = w.vertex2.Y;
                b.M33 = a.M33 = w.vertex3.Y;
                b.M34 = this.owner.centerCoords.Y;

                b.M41 = a.M41 = w.vertex1.Z;
                b.M42 = a.M42 = w.vertex2.Z;
                b.M43 = a.M43 = w.vertex3.Z;
                b.M44 = this.owner.centerCoords.Z;

                a.M24 = this.moveCoords.X;
                a.M34 = this.moveCoords.Y;
                a.M44 = this.moveCoords.Z;

               // Console.WriteLine(b.Determinant.ToString() + "\na: " + a.Determinant.ToString()+"\nb: ");
                float bDeterminant = b.Determinant;
                float aDeterminant = a.Determinant;

                t2 = -1 * (bDeterminant / aDeterminant);

                
                if (t2 > 0 && t2 != float.NaN)
                {
                    if (t2 < t)
                    {
                        positionToHit.X = this.owner.centerCoords.X + this.moveCoords.X * t2;
                        positionToHit.Y = this.owner.centerCoords.Y + this.moveCoords.Y * t2;
                        positionToHit.Z = this.owner.centerCoords.Z + this.moveCoords.Z * t2;

                        //Console.WriteLine(positionToHit.ToString());
                        float[] pass = new float[4] { w.vertex1.X, w.vertex2.X, w.vertex3.X, w.vertex4.X };
                        float x1 = findLargestXYZ(pass) + .1f;
                        pass = new float[4]{ w.vertex1.Y, w.vertex2.Y, w.vertex3.Y, w.vertex4.Y };
                        float y1 = findLargestXYZ(pass) + .1f ;
                        pass = new float[4] { w.vertex1.Z, w.vertex2.Z, w.vertex3.Z, w.vertex4.Z };
                        float z1 = findLargestXYZ(pass) + .1f;
                        pass = new float[4] { w.vertex1.X, w.vertex2.X, w.vertex3.X, w.vertex4.X };
                        float x2 = findSmallestXYZ(pass) - .1f;
                        pass = new float[4] { w.vertex1.Y, w.vertex2.Y, w.vertex3.Y, w.vertex4.Y };
                        float y2 = findSmallestXYZ(pass) - .1f;
                        pass = new float[4] { w.vertex1.Z, w.vertex2.Z, w.vertex3.Z, w.vertex4.Z };
                        float z2 = findSmallestXYZ(pass) - .1f;

                        if (positionToHit.X <= x1 && positionToHit.X >= x2 
                            && positionToHit.Y <= y1 && positionToHit.Y >= y2
                            && positionToHit.Z <= z1 && positionToHit.Z >= z2)
                            {
                                t = t2;
                            }

                    }
                }

            }


            positionToHit.X = this.owner.centerCoords.X + this.moveCoords.X * t;
            positionToHit.Y = this.owner.centerCoords.Y + this.moveCoords.Y * t;
            positionToHit.Z = this.owner.centerCoords.Z + this.moveCoords.Z * t;

        }

        private void hitWall()
        {

            double x = this.centerCoords.X - this.positionToHit.X;
            double y = this.centerCoords.Y - this.positionToHit.Y;
            double z = this.centerCoords.Z - this.positionToHit.Z;




            double distance = Math.Sqrt((x * x + y * y + z * z));

            if (distance <= this.speed)
            {
                this.deathAnimate = true;
            }

        }

        private float findLargestXYZ(float[] a)
        {
            float temp = 0;
            for (int i = 0; i < a.Length; i++)
            {
                if (temp <= a[i])
                {
                    temp = a[i];
                }
            }
            return temp;
        }
        private float findSmallestXYZ(float[] a)
        {
            float temp = float.MaxValue;
            for (int i = 0; i < a.Length; i++)
            {
                if (temp >= a[i])
                {
                    temp = a[i];
                }
            }
            return temp;
        }

    }
}
